Die Bearbeitung von den Werkstücken soll in unterschiedlichen Fertigungszellen durchgeführt werden können.
Die Fertigungszellen können mit unterschiedlichen Maschinen ausgestattet werden. Für diese Maschinen müssen auch leicht unterschiedliche Abläufe für die Bearbeitung vorhanden sein. So kann zum Beispiel für das Beladen und das Entladen der Bearbeitungsmaschinen entweder ein Roboter mit Einfachgreifer oder ein Roboter mit Doppelgreifer benutzt werden. Eine Ablaufsteuerung die für einen Einfachgreifer entwickelt wurde kann zwar auch für einen Doppelgreifer benutzt werden, jedoch wird dann nur ein Greifer des Doppelgreifer genutzt. Um die Doppelgreifer auch voll nutzen zu können, muß die Ablaufsteuerung in der Klasse ProcessingControl geändert werden. Da die Ablaufsteuerung jedoch sehr komplex ist, sind die Änderungen sehr arbeitsaufwendig und fehleranfällig.
Dieser Hotspot wurde mit dem Entwurfsmuster Strategy implementiert. Das Vorgehen für eine Erweiterung entspricht deshalb dem Vorgehen für das Strategy-Muster in [Gamma et. al., 1995].
Aufbau einer Fertigungsstrategie [Peters, 1997]
Der Feinablauf der Fertigungssteuerung wird in der Methode Execute formuliert. Diese Methode ist in der Basisklasse rein virtuell, sie muß daher in einer von Strategy abgeleiteten Klasse definiert werden.
In der Methode Execute muß zunächst die Methode StartSignaler der Klasse ProcessingMediator aufgerufen werden. Dieser Aufruf startet den Signalisierungsmechanismus des Mediators. Er sorgt dafür, daß die sogenannten Fertigmeldungssperren, die von den Methoden der Klasse ProcessingMediator vor der Ausführung eines Auftrags belegt werden, nach Abarbeitung des Auftrags automatisch wieder freigegeben werden. Die Fertigmeldungssperren sind immer dann belegt, wenn der Zustand der realen Objekte der Fertigungszelle vom Zustand der zugehörigen Objekte in OSEFA abweicht. Der Programmierer der Strategie braucht daher nicht selber zu gegebener Zeit eine Fertigmeldungssperre wieder freigeben, er muß lediglich am Anfang der Methode Execute diesen Signalisierungsmechanismus starten. Entsprechend muß am Ende der programmierten Strategie die Methode StopSignaler der Klasse ProcessingMediator aufgerufen werden. Dadurch wird der Signalisierungsmechanismus gestoppt und der damit verbundene Thread verbraucht keine weitere Rechenzeit.
Je nachdem, wie der konkrete Fertigungsablauf aussehen soll, können nun die Methoden der Klasse Strategy bzw. der Klasse ProcessingMediator aufgerufen werden. Prinzipiell ist es möglich, Methoden aus beiden Klassen gemischt zu verwenden. Dies ist jedoch nicht ratsam, da die Methoden der Klasse Strategy implizit die logischen Sperren BufferLock und XactionLock belegen und freigeben, die mit den Methoden der Klasse ProcessingMediator direkt manipuliert werden können. Daher ist es ratsam entweder nur Methoden der Klasse ProcessingMediator oder aber nur Methoden der Klasse Strategy zu verwenden.
Im Folgenden wird beispielhaft beschrieben, welche Methoden zur Formulierung einer Standardstrategie zu verwenden sind. Es werden jeweils die benötigten Methoden der Klasse ProcessingMediator und die alternativ zu verwendenden Methoden der Klasse Strategy aufgeführt.
theMediatorPtr->Wait( Inbuf, XactionLock );
theMediatorPtr->Wait( Inbuf, BufferLock );
if (theMediatorPtr->BufferEmpty( Inbuf ))
theMediatorPtr->GetPallet( Inbuf, WithRaw );
theMediatorPtr->Signal( Inbuf, BufferLock );
theMediatorPtr->Signal( Inbuf, XactionLock );
SetXaction( Inbuf, CloseXaction );
SetXaction( Outbuf,
CloseXaction );
GetPallet( Inbuf, WithRaw );
GetPallet( Outbuf, SpaceForMachined );
while( ( theMediatorPtr->PartStillToMachine()>0 ) && ( !(theMediatorPtr->TerminateGiven(ClearUp)) ) )
if ( InbufXactionOpen == False )
theMediatorPtr->Wait(
Inbuf, XactionLock);
theMediatorPtr->Wait( Inbuf, BufferLock );
theMediatorPtr->Handle( Load, Inbuf );
InbufXactionOpen = False;
theMediatorPtr->Signal(
Inbuf, BufferLock );
theMediatorPtr->Signal( Inbuf, XactionLock );
if ( OutbufXactionOpen == False )
theMediatorPtr->Wait(
Outbuf, XactionLock );
theMediatorPtr->Wait( Outbuf, BufferLock );
if (theMediatorPtr->PalletFull(Outbuf, PartsMachined))
theMediatorPtr->ExchangePallet(Outbuf,SpaceForMachined);
OutbufXactionOpen = True;
theMediatorPtr->Signal(
Outbuf, BufferLock );
if ( OutbufXactionOpen
== False )
theMediatorPtr->Signal( Outbuf, XactionLock );
theMediatorPtr->StartMachining();
theMediatorPtr->IncrementPartsMachined();
if (
InbufXactionOpen == False )
theMediatorPtr->Wait(
Inbuf, XactionLock);
theMediatorPtr->Wait( Inbuf, BufferLock );
if (theMediatorPtr->PalletEmpty(Inbuf, RawParts)
&& (theMediatorPtr->PartsStillToMachine() >
0) )
{
theMediatorPtr->ExchangePallet( Inbuf, WithRaw );
InbuXactionOpen = True;
}
theMediatorPtr->Signal( Inbuf, BufferLock );
if ( InbufXactionOpen == False )
theMediatorPtr->Signal( Inbuf, XactionLock );
if (
OutbufXactionOpen == False )
theMediatorPtr->Wait(
Outbuf, XactionLock );
theMediatorPtr->Wait( Outbuf, BufferLock );
theMediatorPtr->Handle( Unload, Outbuf );
OutbufXactionOpen = False;
theMediatorPtr->Signal(
Outbuf, BufferLock );
theMediatorPtr->Signal( Outbuf, XactionLock );
Handle( Load, Inbuf, CloseXaction );
ExchangePalletConditionally( Outbuf, IfMachinedFull, DontSync, SpaceForMachined, OpenXactionIfExchanged );
theMediatorPtr->StartMachining();
ExchangePalletConditionally( Inbuf, IfRawEmpty, DontSync, WithRaw, OpenXactionIfExchanged );
Handle( Unload, Outbuf, CloseXaction );
InbufXactionOpen = False;
theMediatorPtr->Wait( Inbuf, XactionLock
);
theMediatorPtr->Wait(
Inbuf, BufferLock );
if (!(theMediatorPtr->BufferEmpty( Inbuf )))
theMediatorPtr->StorePallet( Inbuf );
theMediatorPtr->Signal( Inbuf, BufferLock );
theMediatorPtr->Signal( Inbuf, XactionLock );
SetXaction(
Inbuf, CloseXaction );
StorePalletIfAny( Inbuf
);
Aufnahme einer neuen Strategie ins System [Peters, 1997]
Zur Aufnahme einer neuen Strategie in den Baukasten OSEFA, muß man zunächst eine neue Klasse von der abstrakten Basisklasse Strategy ableiten. Im folgenden wird eine neue Strategie NewStrategy angelegt:
class NewStrategy : public Strategy
{
public:
NewStrategy( void );
void Execute( void );
};
In der Klasse wird zunächst ein parameterloser Konstruktor definiert. Dieser wird benötigt, um einige Attribute der Basisklasse, abhängig von der Art der neuen Strategie, auf bestimmte Werte zu setzen. Das Attribut theGripperType bestimmt, ob mit Einfach- oder mit Doppelgreifer gearbeitet wird. Das Attribut theNumberOfBuffers gibt die Anzahl der verwendeten Pufferlager an. Das Attribut staggerType gibt an, ob es sich um versetzten Betrieb handelt oder nicht. (siehe hierzu auch die Beschreibung der Attribute der Klasse Strategy im Kapitel 3.6.4).
Diese drei Attribute dienen OSEFA dazu, abhängig von einem Maschinenauftrag, die richtige Strategie auszuwählen. Um es zu ermöglichen mehrere Strategien im System zu halten, die bezüglich dieser drei Attribute die gleichen Vorrausetzungen erfüllen, wurde noch eine optionale Strategienummer nStrategyNo eingeführt. Wenn diese Nummer einen Wert ungleich Null aufweist, so wird bei der Auswahl der Strategie eine Strategie mit dieser Nummer gewählt.
Zur Definition einer neuen Strategie zur Arbeit mit Einfachgreifer, zur Verwendung von zwei Pufferlagern und für nicht versetzten Betrieb würde der Konstruktor also wie folgt aussehen:
NewStrategy::NewStrategy( void )
{
theGripperType = SingleGripper;
theNumberOfBuffers = 2;
staggerType = NotStaggered;
nStrategyNo = 0;
}
Sollte bereits eine Strategie für diesen Einsatzzweck im System vorhanden sein, so muß eine Nummer für die Strategie vergeben und dem Attribut nStrategyNo zugewiesen werden.
Nun folgt die Implementation der Methode Execute deren Aufbau im Kapitel 3.6.2 beschrieben ist.
Damit ist einen neue Strategie fertiggestellt. Zur Verwendung muß sie nun nur noch dem System bekanntgemacht werden, damit dieses die neue Strategie bei einer Strategieauswahl berücksichtigen kann.
Alle im System enthaltenen Strategien werden von der Klasse StrategyCollection verwaltet. Ein Objekt dieser Klasse wird beim Programmstart erzeugt. Von jeder im System enthaltenen Strategieklasse muß ein Objekt instanziiert werden. Ein Zeiger auf dieses Objekt wird dann über die Methode AppendStrategy in der Klasse StrategyCollection eingetragen.